/******************************************************************************************************
Copyright (C) 2013 Mestrelab Research S.L. All rights reserved.

This file is part of the Mnova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*jslint plusplus: true, continue: true, bitwise: true, nomen: true*/
/*globals settings, Dir,  MessageBox, mainWindow, qsTr, File, Process, FileDialog, print*/

function Python(aVersion, aPath) {
	"use strict";

	this.versionKey = "Python/Version_";
	this.configuration = this.setup(aVersion, aPath);

	if (this.configuration) {
		this.isValid = true;
		this.saveToRegistry();
	} else {
		this.isValid = false;
	}
}

Python.prototype.setup = function (aRequestedVersion, aPath) {
	"use strict";
	var i, j, pythonDirCandidate, pythonDir, path, splittingChar, installation, version, validCandidate,
		standardDir = "C:/Python",
		environment = Process.systemEnvironment().toString();

	if (aPath) {
		pythonDir = new Dir(aPath);
		if (pythonDir.exists) {
			validCandidate = this.validateCandidate(pythonDir, version);
			if (validCandidate) {
				return validCandidate;
			}
			throw "The given path {0} does not correspond to the requested version {1}. Check it and try again".format(aPath, aRequestedVersion);
		} else {
			throw "The given path {0} does not exist. Check it and try again".format(aPath);
		}
	}

	version = this.stringToVersion(aRequestedVersion);
	if (!version.isValid) {
		return;
	}
	installation = this.getInstallationFromRegistry(version);

	if (installation) {
		return installation;
	}

	if (environment.indexOf("ython") !== -1) {

		path = environment.substring(environment.indexOf("Path") + "Path=".length, environment.indexOf(",", environment.indexOf("Path")));

		if (environment.indexOf("OS=Win") !== -1) {
			splittingChar = ";";
		} else {
			splittingChar = ":";
		}
		path = path.split(splittingChar);

		for (i = 0; i < path.length; i++) {
			if (path[i].indexOf("ython") !== -1) {
				pythonDir = new Dir(path[i]);
				validCandidate = this.validateCandidate(pythonDir, version);
				if (validCandidate) {
					return validCandidate;
				}
			}
		}
	}

	for (i = 2; i < 10; i++) {
		for (j = 0; j < 10; j++) {
			pythonDirCandidate = standardDir + i + j;
			pythonDir = new Dir(pythonDirCandidate);
			validCandidate = this.validateCandidate(pythonDir, version);
			if (validCandidate) {
				return validCandidate;
			}
		}
	}

	if (mainWindow.hasGui) {
		MessageBox.information(qsTr("Mnova was unable to find Python {0}.\n If this version is installed please select the installation directory otherwise please install it and restart Mnova").format(aRequestedVersion));
		pythonDirCandidate  = FileDialog.getExistingDirectory(Dir.home(), qsTr("Select Python installation folder"));
		if (pythonDirCandidate && pythonDirCandidate !== "") {
			pythonDir = new Dir(pythonDirCandidate);
			validCandidate = this.validateCandidate(pythonDir, version);
			if (validCandidate) {
				return validCandidate;
			}
		}
	}
};

Python.prototype.saveToRegistry = function () {
	"use strict";
	var currentMajor, currentMinor;

	settings.setValue(this.versionKey + this.configuration.version.version, JSON.stringify(this.configuration));

	currentMajor = settings.value(this.versionKey + this.configuration.version.major, "");

	if (currentMajor) {
		currentMajor = JSON.parse(currentMajor);
		if (currentMajor.version.minor <  this.configuration.version.minor) {
			settings.setValue(this.versionKey + this.configuration.version.major, JSON.stringify(this.configuration));
		} else if (currentMajor.version.minor === this.configuration.version.minor) {
			if (currentMajor.version.revision <  this.configuration.version.revision) {
				settings.setValue(this.versionKey + this.configuration.version.major, JSON.stringify(this.configuration));
			}
		}
	} else {
		settings.setValue(this.versionKey + this.configuration.version.major, JSON.stringify(this.configuration));
		settings.setValue(this.versionKey + this.configuration.version.major + "." + this.configuration.version.minor, JSON.stringify(this.configuration));
	}

	currentMinor = settings.value(this.versionKey + this.configuration.version.major + "."  + this.configuration.version.minor, "");
	if (currentMinor) {
		currentMinor = JSON.parse(currentMinor);
		if (currentMinor.version.revision <  this.configuration.version.revision) {
			settings.setValue(this.versionKey + this.configuration.version.major + "."  + this.configuration.version.minor, JSON.stringify(this.configuration));
		}
	} else {
		settings.setValue(this.versionKey + this.configuration.version.major + "." + this.configuration.version.minor, JSON.stringify(this.configuration));
	}
};

Python.prototype.stringToVersion = function (aVersion) {
	"use strict";
	var version = {major: 0, minor: 0, revision: 0, isValid: true, version: "0.0.0"},
		endOfMajor,
		endOfMinor;

	if (aVersion) {
		version.version = aVersion;
		endOfMajor = aVersion.indexOf(".");
		if (endOfMajor === -1) {
			version.major = parseInt(aVersion, 10);
			if (isNaN(version.major)) {
				version.isValid = false;
			}
			version.minor = "";
			version.revision = "";
		} else {
			version.major = parseInt(aVersion.substring(0, endOfMajor), 10);
			endOfMinor = aVersion.indexOf(".", endOfMajor + 1);
			if (endOfMinor === -1) {
				version.minor = parseInt(aVersion.substring(endOfMajor + 1), 10);
				version.revision = "";
			} else {
				version.minor = parseInt(aVersion.substring(endOfMajor + 1, endOfMinor), 10);
				version.revision = parseInt(aVersion.substring(endOfMinor + 1), 10);
			}
		}
	}
	return version;
};

Python.prototype.checkPythonInstallation = function (aPythonDir, aRequestedVersion) {
	"use strict";

	var installedVersion, message, proc;

	proc = new Process();
	proc.start(aPythonDir + '/python', ['--version']);
	proc.waitForFinished();
	message = proc.allStdOutput + proc.allErrorOutput ;
	installedVersion = this.stringToVersion(message.replace("Python ", ""));

	if (aRequestedVersion.revision && installedVersion.revision !== aRequestedVersion.revision) {
		return false;
	}
	if (aRequestedVersion.minor && installedVersion.minor !== aRequestedVersion.minor) {
		return false;
	}
	if (aRequestedVersion.major && installedVersion.major !== aRequestedVersion.major) {
		return false;
	}
	if (!installedVersion.major) {
		return false;
	}
	return installedVersion;
};

Python.prototype.getInstallationFromRegistry = function (aVersion) {
	"use strict";

	var installation;
	installation = settings.value(this.versionKey + aVersion.version, "");

	if (installation) {
		try {
			installation = JSON.parse(installation);
			if (this.checkPythonInstallation(installation.dir, installation.version)) {
				return installation;
			}
		} catch (ERR1) {print(ERR1); }
	}

	if (aVersion.revision === "") {

		if (aVersion.minor === "") {
			installation = settings.value(this.versionKey + aVersion.major, "");

		} else {
			installation = settings.value(this.versionKey + aVersion.major + "." + aVersion.minor, "");
		}
	}

	if (installation) {
		try {
			installation = JSON.parse(installation);
			if (this.checkPythonInstallation(installation.dir, installation.version)) {
				return installation;
			}
		} catch (ERR2) {print(ERR2); }
	}
};

Python.prototype.validateCandidate = function (aDir, aRequestedVersion) {
	"use strict";

	var tempArray, version;

	if (aDir.exists) {
		tempArray = aDir.entryList("python*");
		if (tempArray.length !== 0) {
			version = this.checkPythonInstallation(aDir.absPath, aRequestedVersion);
			if (version) {
				return {dir: aDir.absPath, version: version, executable: aDir.absPath + "/python"};
			}
		}
	}
};

Python.prototype.toString = function () {
	"use strict";
	return "Python()";
};

Python.prototype.execute = function (aFile, aParameters) {
	"use strict";

	var i, proc, scriptFile, cmd = [];

	scriptFile = new File(aFile);

	if (!scriptFile.exists) {
		scriptFile = new File("ricares:" + aFile);
	}

	if (!scriptFile.exists) {
		return {exitCode: -1, allErrorOutput: "Script file not found", allStdOutput: ""};
	}

	cmd.push(scriptFile.name);
	for (i = 0; i < aParameters.length; i++) {
		cmd.push(aParameters[i]);
	}

	proc = new Process();
	proc.start(this.configuration.executable, cmd);
	proc.waitForFinished(-1);
	return {exitCode: proc.exitCode, allStdOutput: proc.allStdOutput, allErrorOutput: proc.allErrorOutput};
};

Python.prototype.startDetached = function (aFile, aParameters) {
	"use strict";

	var i, proc, scriptFile, cmd = [];

	scriptFile = new File(aFile);

	if (!scriptFile.exists) {
		scriptFile = new File("ricares:" + aFile);
	}

	if (!scriptFile.exists) {
		return {exitCode: -1, allErrorOutput: "Script file not found", allStdOutput: ""};
	}

	cmd.push(scriptFile.name);
	for (i = 0; i < aParameters.length; i++) {
		cmd.push(aParameters[i]);
	}

	proc = new Process();
	proc.start(this.configuration.executable, cmd);
	return proc;
};



